#pragma once

#include <queue>
#include <pthread.h>

const size_t default_capacity = 5;

template<typename T>
class BlockQueue
{
public:
    BlockQueue(const size_t& max_capacity = default_capacity)
        : _maxcap(max_capacity)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&__producer_cond__, nullptr);
        pthread_cond_init(&__consumer_cond__, nullptr);

    }

    void push(const T& in)
    {
        pthread_mutex_lock(&_mutex);
        while (is_full())
        {
            pthread_cond_wait(&__producer_cond__, &_mutex);
        }

        _q.push(in);
        pthread_cond_signal(&__consumer_cond__);
        pthread_mutex_unlock(&_mutex);
    }

    void pop(T* out)
    {
        pthread_mutex_lock(&_mutex);
        
        while (is_empty())
        {
            pthread_cond_wait(&__consumer_cond__, &_mutex);
        }

        *out = _q.front();
        _q.pop();

        pthread_cond_signal(&__producer_cond__);
        pthread_mutex_unlock(&_mutex);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&__producer_cond__);
        pthread_cond_destroy(&__consumer_cond__);
    }

private:
    bool is_empty()
    {
        return _q.empty();
    }
    bool is_full()
    {
        return _q.size() == _maxcap;
    }

private:
    std::queue<T> _q;
    size_t _maxcap;
    pthread_mutex_t _mutex; // 锁
    pthread_cond_t __producer_cond__; //生产者
    pthread_cond_t __consumer_cond__; //消费者
};